查看原文
其他

如何利用App工厂支持创新App

王晓晖彭飞曾庆隆 58技术 2022-03-15


导读

本文主要介绍了如何从一个成熟的App中,逐步进行App工厂改造的。


背景与目标

移动互联网界一向以创新为重,能够在短时间内以矩阵的形式大量推出垂直领域的创新App来不断试错挖掘更多新用户获取更多的流量是目前许多互联网大厂在日益激烈的流量争夺战中保持领先地位的绝招。
许多厂商将自己打造成了一个App工厂批量产出创新App。如头条系的抖音、西瓜视频、火山小视频,腾讯系的腾讯微视、腾讯直播、腾讯动漫等等,这些创新App在短视频最火的风口期迅速涌现出来形成了App矩阵争夺短视频流量,为厂家抢占先机。
图1 头条系创新App矩阵
与利用App工厂打造的App矩阵策略相比基于一个大的平台App进行业务创新的试错成本越来越高,而过高的试错代价严重拖后了业务的创新效率。面对不断增加的创新需求,支持高效产出创新App的能力打造一个App工厂则成为平台未来的演化新方向。
目前支持批量创建移动App的技术方案有不少,普遍的技术方案都是采用嵌入Web的方式使用某种跨平台的研发技术比如RN、Flutter等。这些方案对于创新App的开发或多或少都有一些局限性。

图2 App工厂为创新App提供多种开发框架

在充分利用平台现有技术的大前提下,我们更多考虑基于当前平台现有基础功能,以及Web、RN等框架进行改造,打造一个功能覆盖更加全面的App工厂。能够令创新App的研发能够摆脱单一技术选型的束缚,同时方便地利用平台积累的技术能力并减少额外的功能依赖,提高研发效率。


如何实现上述目标

我们对App的工厂化改造主要是基于现有成熟的平台App现有的功能进行改造。在进行App工厂改造之前,需要针对需求建立起一个合理的App工厂指导框架,然后基于这个框架进行改造。关于App工厂框架介绍可以参阅《App 工厂在 58 集团 App 中的实践(iOS 视角)》来进行深入的理解,框架图如下:

图3 App工厂指导架构

App工厂是一个从无到有的项目,直接对一个成熟的平台App进行App工厂化改造成本高、周期长且短期内无明显收益。且很有可能因缺乏业务需求的支撑而使改造方向出现偏差,导致项目一发而不可收拾。因此App工厂项目需要基于具体实际的需求来边应用边推动,而推动App工厂改造的实际需求主要有如下两类。


1. 基于独立App研发改造App工厂

打造App工厂的重要目的之一就是为了支持创新独立App的研发。为了打造App工厂需要基于现有平台App进行工厂化的改造。独立App的研发需求是基于现有平台App进行工厂化改造的一个重要推动力。

首先我们需要对目标App进行需求的分析,目的是分析出目标App所依赖的基础功能。列举出来作为App工厂首先要提供的基础功能的研发任务列表,完成这些功能并接入到App工厂框架中。其中这个任务列表中的大部分任务都是功能模块的解耦任务。

图4 基于创新App需求推动App工厂改造

简单地说就是在新的独立App研发迭代的过程中,不断地找出需要依赖的基础功能,并将这些功能从平台中解耦出来,改造为独立的基础中间件或业务中间件,接入到事先设定的App工厂大框架中,并将这些改造后的中间件接回到原来的平台App。而新的App则通过App工厂将所需依赖的中间件接入到自己的工程中进行复用。后面的实践介绍中,将会举例详细介绍这个改造过程。


2. 基于垂直业务跨平台研发改造App工厂

支持垂直业务的跨平台研发与维护同样也是目前App工厂的重要目标之一。具体是指为一个独立的业务模块提供基础能力使之同时运行在多个不同的平台。

这就要求我们要将独立业务模块所需的基础功能做更加彻底的解耦,能够随垂直业务迁移到不同的平台中。

同基于独立App研发进行App工厂改造的模式类似,也需要对垂直业务进行分析,分析出业务所依赖的基础功能。列举出来作为App工厂需要提供的基础功能的解耦研发任务列表并完成。


创新App的生成实践

以上两章主要介绍了App工厂项目的诞生背景、目标以及基于现有平台App如何进行App工厂改造的理论基础。接下来将以创新App的实际研发过程来介绍我们是如何一步步利用创新App的需求进行App工厂的改造的,以及如何利用App工厂支持创新App研发的。


1. 建立入口工程

首先按照App工厂指导架构的标准,如图5所示,每一个App都会有一个入口工程,想要创建一个新的创新App首先就要为其创建一个入口工程。

图5 App工厂指导架构

所谓入口工程主要负责对工程池中的Pod进行依赖设置,利用App工厂工程库池内的Pod构建一个新的App工程。同时还包含一些三方SDK的注册id、App包名、自动打包配置脚本等内容。


2. 建立自定义业务Pod

建立了入口工程建立以后,还要为创新App创建一个自定义业务Pod,作为工程第一个引入的Pod。这个Pod中主要存放一些与创新App本身相关联的内容,主要有两类:

一类是我们在开发App的过程中,所做的一些个性化逻辑,例如:自定义的页面、UI组件以及App特有的一些业务功能等。

另一类是App中间件或SDK的配置逻辑,例如:58同城招聘App中需要网络库上报公共参数,那么需要在这个自定义业务Pod中,使用公参中间件的接口获取公参传给网络库。


3. 中间件的解耦

当入口工程与自定义业务Pod建立完成之后,下一步就是利用入口工程将研发所需的招聘业务以及其相依赖的基础中间件与业务中间件引入到创新App的工程中。招聘业务、其依赖的基础中间件和业务中间件都需要从平台App中解耦并提取出来,接入到App工厂框架下,才可以给创新App中使用。

在中间件的解耦过程中面临的主要问题是随着平台App业务越写越多,加上一些历史因素,工程内依赖复杂度越来越高。业务中间件与业务中间件,甚至基础中间件与基础中间件之间都存在着非常错综复杂的依赖关系。如下图6所示:

图6 招聘业务负责的耦合现状

以招聘业务为例,红色的线条标识相互依赖,黑色线条标识单向依赖。由于依赖关系太过复杂,尽管在这里屏蔽了除招聘业务外的所有单向依赖。但是一眼看上去这些错综复杂的依赖关系还是让人眼花缭乱。

由于各个Pod之间的复杂依赖关系,导致我们无法按需对中间件进行引用。为了能顺利将现有成熟业务快速剥离出来接入到创新App,我们必须要对这些不合理的依赖关系进行解耦,这样才能按需为创新App接入真正需要的中间件。


4. 明确解耦需求

中间件的解耦首先要明确中间件的解耦需求,产出任务列表。创新App仅需要部分平台App本体所包含的基础与业务中间件。一开始就去解耦平台App的所有中间件进行App工厂改造周期长,会阻塞创新App的研发进度。

要从App需要的业务入手,分析其依赖情况,按照需求一点一点地去做这个拆分。这样做不仅不会对创新App的研发产生阻塞,还可以在满足创新App研发需求的前提下,降低风险。对创新App所涉及的业务做一次梳理,找出业务主要的需要功能,列出最优先的解耦任务。比如:在58同城招聘App中我们通过需求的梳理,得出58同城招聘App需要使用到平台App的IM、网络库、工具库、登录库、缓存库、HybirdSDK、跳转中心等多个底层功能。

接下来把这些底层功能的解耦列为优先要完成的任务。然后我们再根据不同的情况通过各种解耦手段首先完成这些基础功能的解耦。


5. 选择合适的解耦手段

在App工厂项目的改造过程中,我们常用的中间件解耦手段大概有以下三大类型,分别是:构建协议层、基础功能下沉以及生成代码变种。

5.1 构建协议解耦

所谓构建协议解耦,就是设法把一个功能模块对外部依赖的功能抽象成一个协议。是原有模块对外部的依赖转换为对协议的依赖。协议可以是一个类固定的API、可以是一个路由连接、也可以是类别方法、还可以是一个protocol规定的API。根据业务场景,一般使用在可选功能依赖的解耦中。

构建协议结构的方式方法有很多,比如:

- 直接利用运行时API

如上图所示,是一种简单的利用运行时API构建协议的方式。通常用来解决API固定、逻辑简单且非必需的功能的依赖(也就是某一个方法调得到调不到都不会影响到原本的结果)。这种方式虽然方便但是具有隐蔽。无法通过编译器的语法检测感知到协议的变化,为后期维护带来困扰。

图7 构建运行时解耦代码示例

- 利用自定义路由协议

所谓自定义路由协议也就是页面间的跳转协议,通常使用在以页面为维度的功能模块之间解耦。通过跳转中心,连接两个页面之间交互。利用路由协议解耦的可以使模块间的依赖转为对跳转协议的依赖,但是是它只限于较为完整且以页面为维度的模块。

- 利用类别

使用类别的方式类实际上也属于利用了运行时API。利用类别主要是解决模块中产生了对外依赖的功能,但这部分功能与模块的核心功能并无关系,属于平台App自身的业务功能。以相册功能为例:

由于平台App的需要,相册模块内会有一系列的页面埋点逻辑,这就导致了相册模块依赖埋点模块。但这些埋点逻辑与相册核心功能无关。为了满足平台App对埋点的需求,同时又要解除相册对埋点功能的依赖。于是将埋点相关逻辑迁移到了类别中从而解决了相册模块与埋点模块的依赖。

图8 类别解耦代码示例

- 利用Protocol

使用Protocol先规范模块对外依赖的接口,使模块对外部模块的依赖改变为模块自身规范的接口的依赖。这种方式其实也是我们在App工厂项目中最为通用的方式,这种方式明确规范了模块外部依赖的方式,同时增加了模块底层可替换性。

图9 耦合了平台埋点工具的中间件


图10 利用Protocol解耦代码示例


- 利用IOC的模式

IOC模式是一种比较通用解决的模块依赖方法,是一种更加高级的使用Protocol的方式。通过协议接口和IOC容器使两个模块间的依赖转为两个模块对协议接口与IOC容器的依赖。从而解除模块之间的依赖。如图:

图11 利用IOC工厂模式解耦

IOC的模式一般都是解耦相对独立且标准的功能模块。

图12 利用IOC模式解耦代码示例

5.2 基础功能下沉解耦

所谓基础功能下沉,是指将模块间依赖的一些标准化功能从模块中抽离出来,作为基础功能迁移到模块在框架中所处位置的下层。如下图所示:

图13 利用基础功能下沉的方式解耦

由于模块B依赖模块A中的功能A,导致模块B依赖了模块A。经过分析功能A实际上是一个比较独立的基础功能,所以可以将功能A提取出来放入基础功能模块中。这样模块A与模块B依赖下层的基础功能模块,AB之间的依赖关系便解除了。

5.3 生成代码变种解耦

生成代码变种是解耦过程中低频率用到的方法,简单来说就是将同一份代码拷贝至不同的功能模块由不同的功能模块自己维护。这种做法对于一个独立的功能模块来说是最快捷的解耦方式,但是会带来代码的膨胀。所以一般仅会使用此方法解决非常简单功能的依赖。比如获取字符串尺寸、获取当前日期的特定格式字符串等功能。

图14 生成代码变种代码示例


图15 生成代码变种代码示例

-解耦实践

灵活利用以上解耦的手段在App工厂中的架构指导下来进行中间件的解耦,是推动App工厂改造的关键,也是一定时期内App工厂项目支持创新App研发的重要一步。接下来举一个实际的例子来介绍为我们是如何进行基础中间件和业务中间件解耦的。

-明确架构准则

解耦前首先要将梳理出来创新App所依赖的底层中间件,并按照App工厂架构标准明确出App的基本架构,以58同城招聘为例: 

图16 58同城招聘创新App的指导架构图

按照如上的结构,我们明确了所需要解耦的中间件以及他们所属的层级,按照App工厂的依赖标准:

  • 上层可以依赖下层,但下层不可以依赖上层。

  • 可以隔层依赖。比如业务pod可以依赖基础库pod;

  • 业务pod间不能产生依赖。比如房产pod不能依赖招聘pod。

  • 中间件pod和三方库pod可以单向依赖。比如RN所在pod可以对登录服务所在pod产生单向依赖。

我们先后进行标准中间件、业务中间件以及招聘公共业务的解耦。

-标准中间件的解耦

作为基础中间件,其一定不需要依赖外部功能便可以满足自身的核心功能。也就是说它必须是较为独立的。当我们定义一个组件为基础中间件之后,那么我们在解耦的时候就要尽量剔除中间件对外部的依赖。

以网络中间件为例,网络中间件的核心功能为网络请求,网络状态的判断等。这些功能无需外部依赖,底层仅依赖系统API,是标准功能,所以网络中间件应该是一个标准中间件。

而当下的网络中间件还有一个收集公共参数的逻辑,它是属于平台的业务逻辑,需要依赖很多获取产生公共参数的外部业务模块。导致网络中间件对外部产生了依赖。

要解决网络中间件对外部产生的依赖就得将收集公共参数的逻辑转移待外部。而统一配置公共参数是实在的业务需求。我们的方法是为网络中间件增加一个获取公参的协议,网络中间件通过协议获取公参,将收集公共参数的逻辑转移到了每个App的自定义业务Pod中。这样就断开了网络中间件与外部业务逻辑的依赖,保证了网络库的标准。

图17 网络库通过协议解耦

-业务中间的解耦

根据App工厂对于业务中间件的定义,业务中间件本不适用于其他App。但是这个也要根据需求灵活调整。如果一个业务中间件可以只依赖标准中间件与基础库,那么这个业务中间件也是可以应用一个在独立的App上的。

按照App工厂的标准,业务中间件可以直接依赖标准中间,看上去降低了解耦的解耦工作的难度。但是业务中间件的依赖情况比标准中间件要复杂得多,一般业务中间件解耦首先要把其依赖的标准中间件解耦出来,以公共列表中间件解耦为例:

我们通过分析发现公共列表中间件依赖了网络中间件、缓存中间件、定位中间件等多个中间件,那么我们要完成公共列表中间件解耦的前提是将它依赖的标注中间件解耦。

同时在分析公共列表中间依赖情况时,我们发现了列表中间件中依赖了很多其他业务Pod中的代码。其中标准的功能有通用的宏定义、固定全局参数如App版本号这种,我们为这种通用的标准功能临时建立了一个工具标准中间件。还有一些偏业务的功能比如页面追踪埋点、IM等需要建立协议进行解耦。

图18 列表中间件的解耦

其中,页面追中埋点我们使用了分类的方式。一方面原本的页面追踪埋点逻辑依赖了外部业务Pod的内容。另一方面对于公共列表中间件来说页面追踪逻辑并不影响它的功能使用,属于可有可无的功能。所以在这里我们使用了分类,为公共列表页页面添加一个追踪埋点逻辑的分类,只在平台App中引用。这样其他App在引用公共列表中间件时,就不用关心页面追踪埋点的依赖问题了。

而公共列表中间件对于IM的依赖,只是使用IM获取一些未读消息数之类的内容。获取IM未读消息数属于一个通用的协议,我们在这里将它定义为全局参数协议的一个,使用IOC的方式来解决。让公共列表页通过公共的IOC协议来获取IM数。

除了以上这两种依赖,还有其他的依赖的解决但都没有脱离上述解耦方法的范围。

-公共业务解耦

公共业务其实就是指创新App所需要复用的业务中的部分,这部分代码的解耦工作与业务中间件的解耦非常相似,但是它可以直接依赖业务中间件。

整个解耦的过程中,需要我们具体情况具体分析,不能保证每个问题都能固定对应一种方法解决,可以灵活选择。我们的主要目的还是以App工厂框架为指导,逐步将现有平台App改造成App工厂框架,以支持创新App的研发需要。


6. 利用引入App工厂提供的中间件

当我们将一个创新App所需的中间件解耦完成之后,再将这些解耦后的中间件基于App工厂的整体框架接入到创新App中。这样一个App工厂对于创新App的研发支持就基本完成了。

图19 58二手车直接使用已完成解耦的公共业务与中间件Pod构建工程


如何处理App工厂所不支持的业务

早在App工厂项目之前,我们曾完成过一个马赛克项目,主要目的就是想利用一个简单的App框架预先开发多套视图模板快速组合生成不同的App。

试图提前开发出一批可能用到的视图控件,通过配套的马赛克后台勾选完成控件模板的组合自动生成App工程并打出对应的App包。同时通过不断开发提交模板代码来拓展App的样式。它的App端的大体架构是这样的:

图20 马赛克项目架构图

马赛克项目的App工程一共分为四个层级分别是入口工程、业务层、服务层与公共库。

其中入口工程是App的壳工程,包含了App的构造器、App组件配置表以及代码资源混淆器。App构造器利用App组件配置表获取到App组件配置信息将App工程构造出来。App组件配置表中包含了要引入哪些组件、App的各种三方Appid、版本号、App名字等等,组件配置表是由马赛克后台根据使用者填写的内容自动生成的。而代码/资源混淆器则是在最终打包前执行混淆的脚本,为了提高App的安全性。

业务层则是包含了马赛克项目App的四种具体业务类型:Tab也就是App的视图主结构,通过读取配置来自动组合首页、列表以及其他已经做好的页面组件。而首页、列表、详情则是三级动态列表页面,他们都具备根据后端下发数据协议来动态展示UI组件模板的能力。

服务层则是提供的一些封装好的基础服务功能,比如定位服务、登录服务、H5混合支持的Hybird服务等等。这些封装好的功能,通过IOC的形式被上层的业务调用,从而支持通过马赛克后台生成的配置表动态地引入到工程中。

公共库层则是App最底层,提供了一些三方库SDK、基础功能库、UI库等最基础的能力。

通过马赛克项目实现了后台配置App的信息、选择模板与服务功能一键生成App的能力。但是马赛克项目也有其弱点,马赛克项目只能选择已有模板与服务功能进行App的组合,产生的App简单且重复度高无法通过AppStore的审核。

图21 为创新App创建个性化业务Pod

对于创新业务来说,高度自定义研发是高需求,创新业务创意是无限的,而马赛克需要按照一套死板的流程进行模板和服务功能的研发,显然不适合创新App的研发需求的。所以后来设计App工厂的框架时,为了满足创新App的研发需求。在App工厂项目里,我们为每一个创新App都单独设计了一个自定义业务Pod。每个创新App都可以将自己的个性化业务放在这里单独维护。比如个性化的UI、试验性的产品业务等。创新App真正需要开发的部分就在这里面,利用App工厂提供的基础能力,可以随意开发想开发的功能。


如何保证创新App迭代效率和质量

目前在App工厂项目支持下一共维护了58同城招聘、58二手车、58同城租房这三个创新App的研发与迭代,同时在创新App的不断迭代过程中产出了大量的业务与基础中间件。保证这些中间件的稳定才能保证创新App的质量。随着平台随业务的迭代,这些已经经过App工厂项目解耦的中间件又有着再次引入不合理依赖的风险。


1. 维持中间件稳定

我们在研发过程中产出的中间件的成果需要持续去维持,以避免后面的平台研发中引入新的耦合。目前我们采取了两种手段:

1.1 耦合检测工具

我们研发了耦合检测的工具,以Pod为依赖单位,遍历每一个Pod的文件读取并分析其文件引用是否对错误的Pod文件有错误的引用,并将错误引用记录下来。每天定时使用工具进行检测,完成后如果发现有耦合问题,就会发送邮件警告给组内相关同学。

1.2 将耦合情况引入代码Review机制

除了自动化检测外,我们还要在日常CodeReview的流程中加入代码依赖的审查。这样既可以保证中间件的成果不被新的外部依赖污染,又可以在平时将App工厂的框架规范推广给大家。


2. 扩大中间件解耦范围

已经完成的创新App需要不断进行迭代,为了保证迭代效率。需要我们根据创新App业务可能使用到的功能去梳理出更多的业务、标准中间件的研发任务持续对平台App进行App工厂改造。


3. 实现自动化创建创新App

当App工厂项目进行到后期,将考虑开发配套的后台来支持一键生成创新App,更进一步提升创新App的研发效率。

在App工厂改造过程中,积累了很多的中间件与公共的垂直业务。可以通过将这些公共的垂直业务通过入口工程的配置文件进行自由组合,产生各种满足不同需求的创新App的开发工程

 

图22 通过配置后台生成创新App入口工程

创新App的研发人员通过后台勾选,选择自己的创新App需要的垂直业务模块(后台会根据业务的依赖情况自动选择关联的各中间件)与中间件,通过后台自动创建出创新App的模板入口工程与自定义业务Pod。

App工厂的主要目的是将现有成熟的平台App逐步改造成一个能够支持创新App研发的App工厂,尽可能地利用平台积累的技术成功,降低创新App的研发成本。创新App的研发与App工厂的改造互相推动,基于创新App的研发需求,在有限的资源下逐步完成对一个成熟的平台App中进行App工厂化改造的实践过程。

目前App工厂在创新App时还有很多需要手动配置的地方,新的创新App工程在生成还没有达到完全的自动化,这也是App工厂项目未来需要不断完善的地方。相信App工厂项目还会产出更多的创新App而且效率会越来越高。

另外在此真诚地感谢租房、安居客、招聘、二手车团队的老板和同学们的鼎力支持和同心协作,才使得App工厂项目克服重重困难完成了自身的进化同时产出了这么多的创新App成果。


作者简介

王晓晖、彭飞、曾庆隆


推荐阅读

租房业务APP后端服务重构之路

58App-Android端的动态化框架实践与思考

“暗黑模式”之58 同城 iOS App深色模式适配实践

开源|Zucker:Android APP模块化大小自动分析统计工具

开源|WBBlades:基于Mach-O文件解析的APP分析工具


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存